/* eslint-disable */

import type { ColumnType, DataProcessConfig, GroupingConfig, TCompareSymbol } from './types';
import { dayjs } from 'element-plus';
import { h } from 'vue';

/*#region radash */

/**
 * 这部分函数来自 radash
 * @link 仓库：https://github.com/sodiray/radash
 * @link 文档：https://radash-docs.vercel.app/docs/getting-started
 */

export const isDate = (value: any): value is Date => {
	return Object.prototype.toString.call(value) === '[object Date]';
};
export const isNumber = (value: any): value is number => {
	try {
		return Number(value) === value;
	} catch {
		return false;
	}
};
export const isFunction = (value: any): value is Function => {
	return !!(value && value.constructor && value.call && value.apply);
};
export const isSymbol = (value: any): value is symbol => {
	return !!value && value.constructor === Symbol;
};

export const isEmpty = (value: any) => {
	if (value === true || value === false) return true;
	if (value === null || value === undefined) return true;
	if (isNumber(value)) return value === 0;
	if (isDate(value)) return isNaN(value.getTime());
	if (isFunction(value)) return false;
	if (isSymbol(value)) return false;
	const length = (value as any).length;
	if (isNumber(length)) return length === 0;
	const size = (value as any).size;
	if (isNumber(size)) return size === 0;
	const keys = Object.keys(value).length;
	return keys === 0;
};

export const toggle = <T>(
	list: readonly T[],
	item: T,
	toKey?: null | ((item: T, idx: number) => number | string | symbol),
	options?: {
		strategy?: 'prepend' | 'append';
	}
) => {
	if (!list && !item) return [];
	if (!list) return [item];
	if (!item) return [...list];
	const matcher = toKey ? (x: T, idx: number) => toKey(x, idx) === toKey(item, idx) : (x: T) => x === item;
	const existing = list.find(matcher);
	if (existing) return list.filter((x, idx) => !matcher(x, idx));
	const strategy = options?.strategy ?? 'append';
	if (strategy === 'append') return [...list, item];
	return [item, ...list];
};

export const omit = <T, TKeys extends keyof T>(obj: T, keys: TKeys[]): Omit<T, TKeys> => {
	if (!obj) return {} as Omit<T, TKeys>;
	if (!keys || keys.length === 0) return obj as Omit<T, TKeys>;
	return keys.reduce(
		(acc, key) => {
			delete acc[key];
			return acc;
		},
		{ ...obj }
	);
};

export const select = <T, K>(array: readonly T[], mapper: (item: T, index: number) => K, condition: (item: T, index: number) => boolean) => {
	if (!array) return [];
	return array.reduce((acc, item, index) => {
		if (!condition(item, index)) return acc;
		acc.push(mapper(item, index));
		return acc;
	}, [] as K[]);
};

export const template = (str: string, data: Record<string, any>, regex = /\{\{(.+?)\}\}/g) => {
	return Array.from(str.matchAll(regex)).reduce((acc, match) => {
		return acc.replace(match[0], data[match[1]]);
	}, str);
};

export const isPromise = (value: any): value is Promise<any> => {
	if (!value) return false;
	if (!value.then) return false;
	if (!isFunction(value.then)) return false;
	return true;
};

export const tryit = <Args extends any[], Return>(func: (...args: Args) => Return) => {
	return (...args: Args): Return extends Promise<any> ? Promise<[Error, undefined] | [undefined, Awaited<Return>]> : [Error, undefined] | [undefined, Return] => {
		try {
			const result = func(...args);
			if (isPromise(result)) {
				return result.then((value) => [undefined, value]).catch((err) => [err, undefined]) as Return extends Promise<any>
					? Promise<[Error, undefined] | [undefined, Awaited<Return>]>
					: [Error, undefined] | [undefined, Return];
			}
			return [undefined, result] as Return extends Promise<any> ? Promise<[Error, undefined] | [undefined, Awaited<Return>]> : [Error, undefined] | [undefined, Return];
		} catch (err) {
			return [err as any, undefined] as Return extends Promise<any> ? Promise<[Error, undefined] | [undefined, Awaited<Return>]> : [Error, undefined] | [undefined, Return];
		}
	};
};

export const merge = <T>(root: readonly T[], others: readonly T[], matcher: (item: T) => any) => {
	if (!others && !root) return [];
	if (!others) return root;
	if (!root) return [];
	if (!matcher) return root;
	return root.reduce((acc, r) => {
		const matched = others.find((o) => matcher(r) === matcher(o));
		if (matched) acc.push(matched);
		else acc.push(r);
		return acc;
	}, [] as T[]);
};

/*#endregion */

/**
 * 比较操作符执行的函数映射
 */
export const CompareSymbol: Record<string, (...args: any[]) => boolean> = {
	equal: (a: string | number, b: string | number) => a === b,
	notEqual: (a: string | number, b: string | number) => a !== b,
	greaterThan: (a: number, b: number) => a > b,
	greaterThanOrEqual: (a: number, b: number) => a >= b,
	lessThan: (a: number, b: number) => a < b,
	lessThanOrEqual: (a: number, b: number) => a <= b,
	isEmpty: isEmpty,
	isNotEmpty: (value: any) => !isEmpty(value),
	includes: (a: string, b: string) => a.includes(b),
	notIncludes: (a: string, b: string) => !a.includes(b),
	isBefore: (a: string | number | Date, b: string | number | Date) => dayjs(dayjs(a).format('YYYY-MM-DD')).isBefore(dayjs(b).format('YYYY-MM-DD')),
	isAfter: (a: string | number | Date, b: string | number | Date) => dayjs(dayjs(a).format('YYYY-MM-DD')).isAfter(dayjs(b).format('YYYY-MM-DD')),
};

export const CompareSymbolLabel: { [key: string]: any } = {
	equal: {
		number: '=',
		default: '等于',
	},
	notEqual: {
		number: '!=',
		default: '不等于',
	},
	lessThan: {
		default: '<',
	},
	lessThanOrEqual: {
		default: '<=',
	},
	greaterThan: {
		default: '>',
	},
	greaterThanOrEqual: {
		default: '>=',
	},
	isEmpty: {
		default: '为空',
	},
	isNotEmpty: {
		default: '不为空',
	},
	includes: {
		default: '包含',
	},
	notIncludes: {
		default: '不包含',
	},
	isBefore: {
		default: '早于',
	},
	isAfter: {
		default: '晚于',
	},
};

/**
 * 不同类型的列在筛选时可以选择的比较操作符
 */
export const SelectableCompareSymbol: Record<ColumnType, TCompareSymbol[]> = {
	text: ['includes', 'notIncludes', 'equal', 'notEqual', 'isEmpty', 'isNotEmpty'],
	enum: ['includes', 'notIncludes', 'equal', 'notEqual', 'isEmpty', 'isNotEmpty'],
	date: ['equal', 'notEqual', 'isBefore', 'isAfter', 'isEmpty', 'isNotEmpty'],
	number: ['equal', 'notEqual', 'lessThan', 'lessThanOrEqual', 'greaterThan', 'greaterThanOrEqual', 'isEmpty', 'isNotEmpty'],
};

/**
 * 日期筛选选项
 */
export const DateConditions = [
	{ label: '具体日期', value: 'specificDate' },
	{ label: '今天', value: 'today' },
	{ label: '明天', value: 'tomorrow' },
	{ label: '昨天', value: 'yesterday' },
	{ label: '本周', value: 'thisWeek' },
	{ label: '上周', value: 'lastWeek' },
	{ label: '本月', value: 'thisMonth' },
	{ label: '上月', value: 'lastMonth' },
	{ label: '过去7天内', value: 'withinThePast7Days' },
	{ label: '未来7天内', value: 'withinTheNext7Days' },
	{ label: '过去30天内', value: 'withinThePast30Days' },
	{ label: '未来30天内', value: 'withinTheNext30Days' },
];

/**
 * 日期筛选选项对应的比较值
 */
export const DateConditionValue = {
	today: () => dayjs().valueOf(),
	tomorrow: () => dayjs().add(1, 'day').endOf('day').valueOf(),
	yesterday: () => dayjs().subtract(1, 'day').startOf('day').valueOf(),
	thisWeek: () => [dayjs().startOf('week').valueOf(), dayjs().endOf('week').valueOf()],
	lastWeek: () => [dayjs().subtract(1, 'week').startOf('week').valueOf(), dayjs().subtract(1, 'week').endOf('week').valueOf()],
	withinThePast7Days: () => dayjs().subtract(7, 'day').startOf('day').valueOf(),
	withinTheNext7Days: () => dayjs().add(7, 'day').endOf('day').valueOf(),
	withinThePast30Days: () => dayjs().subtract(30, 'day').startOf('day').valueOf(),
	withinTheNext30Days: () => dayjs().add(30, 'day').endOf('day').valueOf(),
};

/**
 * 逻辑操作符
 */
export const LogicSymbol = {
	AND: (args: boolean[]) => args.every((arg) => arg),
	OR: (args: boolean[]) => args.some((arg) => arg),
};

/**
 * 输入一个数值，返回带有css单位的字符串，默认单位为 px
 */
export const numberRegex = /^\d+$/;
export const withDefaultUnit = (value: string | number, unit = 'px'): string => {
	const _value = String(value);
	return numberRegex.test(_value) ? `${_value}${unit}` : _value;
};

/**
 * 防抖函数
 */
export function debounce<T extends (...args: any[]) => void>(fn: T, delay: number): (...args: Parameters<T>) => void {
	let timeoutId: ReturnType<typeof setTimeout> | undefined;
	return function (this: any, ...args: Parameters<T>): void {
		if (timeoutId) clearTimeout(timeoutId);
		timeoutId = setTimeout(() => {
			fn.apply(this, args);
		}, delay);
	};
}

/**
 * 标签色板
 * 用于枚举类型列显示标签样式枚举值、筛选面板的枚举值选项
 */
export const TagColorList = [
	'#93c5fd',
	'#c4b5fd',
	'#d8b4fe',
	'#f0abfc',
	'#f9a8d4',
	'#fda4af',
	'#a5b4fc',
	'#fde047',
	'#bef264',
	'#7dd3fc',
	'#67e8f9',
	'#86efac',
	'#6ee7b7',
	'#fca5a5',
	'#fcd34d',
	'#fdba74',
	'#d6d3d1',
	'#d4d4d8',
	'#cbd5e1',
];

export const typeIcon: Record<ColumnType, string> = {
	text: 'i-lucide-clipboard-type',
	number: 'i-tabler-number-100-small scale-125',
	date: 'i-lucide-clock-2',
	enum: 'i-lucide-tags',
};

export interface PromiseResolver<T> {
	promise: Promise<T>;
	resolve: (value: T | PromiseLike<T>) => void;
	reject: (reason?: any) => void;
}

export const noop = () => {};

export const promiseResolver = <T = any>(): PromiseResolver<T> => {
	let resolve: PromiseResolver<T>['resolve'] = noop;
	let reject: PromiseResolver<T>['reject'] = noop;
	const promise = new Promise<T>((res, rej) => {
		resolve = res;
		reject = rej;
	});
	return { promise, resolve, reject } as PromiseResolver<T>;
};

export // 排序描述内容
const SortingDescription = (config: GroupingConfig, order: 'asc' | 'desc'): VNode => {
	switch (config.refCol?.type ?? '') {
		case 'text':
			return h(
				'div',
				{ class: 'flex gap-x-2 items-center' },
				{
					default: () => {
						return [h('span', {}, order === 'asc' ? 'A' : 'Z'), h('i', { class: 'i-lucide-arrow-right' }), h('span', {}, order === 'asc' ? 'Z' : 'A')];
					},
				}
			);
		case 'number':
		case 'date':
			return h(
				'div',
				{ class: 'flex gap-x-2 items-center' },
				{
					default: () => {
						return [h('span', {}, order === 'asc' ? '0' : '9'), h('i', { class: 'i-lucide-arrow-right' }), h('span', {}, order === 'asc' ? '9' : '0')];
					},
				}
			);
		case 'enum':
			return h('span', {}, { default: () => (order === 'asc' ? '选项正序' : '选项逆序') });
	}
};

export const deepClone = <T>(data: T): T => {
	if (typeof data !== 'object' || data === null) return data;
	const cloneObject: T = (Array.isArray(data) ? [] : {}) as T;
	for (const key in data) {
		if (Object.prototype.hasOwnProperty.call(data, key)) {
			if (typeof data[key] === 'function') {
				cloneObject[key] = data[key];
			} else {
				cloneObject[key] = deepClone(data[key]);
			}
		}
	}
	return cloneObject;
};

export const defaultDataProcessConfig: () => DataProcessConfig = () => ({
	filter: {
		logicSymbol: null,
		conditions: [],
	},
	sorter: [],
	grouping: [],
});
